//package com.hospital.bill.module.service.impl;
//
//import com.alibaba.fastjson.JSON;
//import com.baomidou.mybatisplus.mapper.EntityWrapper;
//import com.baomidou.mybatisplus.service.impl.ServiceImpl;
//import com.hospital.bill.module.utils.DictUtils;
//import com.hospital.bill.module.controller.dto.BillDto;
//import com.hospital.bill.module.controller.dto.ClinicItem;
//import com.hospital.bill.module.controller.dto.ClinicType;
//import com.hospital.bill.module.mapper.*;
//import com.hospital.bill.module.model.*;
//import com.hospital.bill.module.service.BillService;
//import com.hospital.bill.module.utils.RedisUtils;
//import lombok.extern.slf4j.Slf4j;
//import org.springframework.stereotype.Service;
//import org.springframework.transaction.annotation.Transactional;
//
//import javax.annotation.Resource;
//import java.util.*;
//import java.util.stream.Collectors;
//
///**
// * @author lvyao
// * @description 扣费实现类
// * @date 2020/5/7
// */
//@Service
//@Transactional(rollbackFor = Exception.class)
//@Slf4j
//public class BillServiceImpl extends ServiceImpl<OutpPriceMapper, OutpBillitem> implements BillService {
//
//    private final String PATIENT_KEY = "patient_id_%s";
//    @Resource
//    private ClinicItemClassMapper clinicItemClassMapper;
//    @Resource
//    private PatientRemainMapper patientRemainMapper;
//    @Resource
//    private QueryClinicItemMapper queryClinicItemMapper;
//    @Resource
//    private OutpPriceMapper outpPriceMapper;
//
//    @Resource
//    private InpPriceMapper inpPriceMapper;
//
//    @Resource
//    private QueryBillItemPriceMapper queryBillItemPriceMapper;
//
//    @Resource
//    private ChargeTypeMapper chargeTypeMapper;
//
//    @Override
//    public boolean outpBillOperation(BillDto billDto) {
//        List<OutpBillitem> outpBillItemList = new ArrayList<>();
//        if (billDto != null && billDto.getClinicTypeList() != null && billDto.getClinicTypeList().size() > 0) {
//            for (ClinicType item : billDto.getClinicTypeList()) {
//                boolean isPackage = DictUtils.getClinicItemClassList().stream().anyMatch(c -> c.getClassCode().equals(item.getType()));
//                for (ClinicItem item1 : item.getClinicItemList()) {
//                    {
//                        if (isPackage) {
//                            EntityWrapper<ClinicItemBill> clinicItemBillEntityWrapper = new EntityWrapper<>();
//                            clinicItemBillEntityWrapper.eq("clinic_item_class", item.getType()).eq("clinic_item_code", item1.getClinicItemCode());
//                            List<ClinicItemBill> clinicItemBills = queryClinicItemMapper.selectList(clinicItemBillEntityWrapper);
//
//                            outpBillItemList.addAll(clinicItemBills.stream().map(c -> setNewObject(c.getChargeItemClass(), c.getChargeItemCode(), c.getAmount() * item1.getAmount(),
//                                    item1.getPerformedBy(), billDto.getVisitNo(), billDto.getVisitDate(), c.getChargeItemNo())).collect(Collectors.toList()));
//                        } else {
//                            outpBillItemList.add(setNewObject(item1.getItemClass(), item1.getPriceCode(), item1.getAmount(), item1.getPerformedBy(), billDto.getVisitNo(), billDto.getVisitDate(), "1"));
//                        }
//                    }
//                }
//            }
//            List<OutpBillitem> outpBillDetail = getOutpBillDetail(outpBillItemList, billDto.getChargeType());
//            List<ChargePriceSchedule> chargePriceScheduleList = DictUtils.getChargePriceScheduleList();
//            ChargePriceSchedule chargePriceSchedule = chargePriceScheduleList.stream().filter(c -> c.getChargeType().equals(billDto.getChargeType()) && "1".equals(c.getChargeSpecialIndicator())).findFirst().orElse(null);
//            List<OutpBillitem> outpBillitemList = new ArrayList<>();
//            List<OutpBillitem> outpBillitemList1 = new ArrayList<>();
//
//            if (chargePriceSchedule != null) {
//                outpBillitemList = (List<OutpBillitem>) canculateChargesExcept(outpBillDetail, billDto.getChargeType(), billDto.getOutpOrinp());
//                outpBillitemList1 = (List<OutpBillitem>) canculateChargesSpecial(outpBillDetail, billDto.getChargeType(), billDto.getOutpOrinp());
//                if (outpBillDetail != null && outpBillDetail.size() > 0) {
//                    for (OutpBillitem outpBillitem : outpBillDetail) {
//                        outpBillitem.setCharges(Math.round(outpBillitem.getCosts() * outpBillitem.getAmount() * (chargePriceSchedule.getPriceCoffNumerator() / chargePriceSchedule.getPriceCoffDenominator()) * 100) * 0.01d);
//                        outpBillitem.setCosts(Math.round(outpBillitem.getCosts() * outpBillitem.getAmount() * 100) * 0.01d);
//                    }
//                }
//            } else {
//                if (outpBillDetail != null && outpBillDetail.size() > 0) {
//                    for (OutpBillitem outpBillitem : outpBillDetail) {
//                        outpBillitem.setCharges(Math.round(outpBillitem.getCosts() * outpBillitem.getAmount() * 100) * 0.01d);
//                        outpBillitem.setCosts(Math.round(outpBillitem.getCosts() * outpBillitem.getAmount() * 100) * 0.01d);
//                    }
//                }
//            }
//            outpBillDetail.addAll(outpBillitemList1);
//            outpBillDetail.addAll(outpBillitemList);
//            DoubleSummaryStatistics total = null;
//            if (outpBillDetail != null && outpBillDetail.size() > 0) {
//                total = outpBillDetail.stream().mapToDouble(BillItem::getCharges).summaryStatistics();
//                //锁住用户信息
//                if (RedisUtils.tryGetDistributedLock(String.format(PATIENT_KEY, billDto.getPatientId()), "1", 10000)) {
//                    try {
//                        PatientRemain outpPatientRemain = patientRemainMapper.getOutpPatientRemain(billDto.getPatientId());
//                        if (total.getSum() > outpPatientRemain.getPrepayRemain()) {
//                            for (OutpBillitem outpBillitem : outpBillDetail) {
//                                Integer insert = outpPriceMapper.insert(outpBillitem);
//                                if (insert < 1) {
//                                    log.info("插入门诊费用明细异常：" + JSON.toJSONString(outpBillitem));
//                                    throw new RuntimeException("当前费用插入异常，请联系管理员查看日志");
//                                }
//                            }
//                            return true;
//                        } else {
//                            throw new RuntimeException("余额不足请充值");
//                        }
//                    } catch (Exception e) {
//                        throw new RuntimeException(e.getMessage());
//                    } finally {
//                        RedisUtils.releaseDistributedLock(String.format(PATIENT_KEY, billDto.getPatientId()), "1");
//                    }
//                } else {
//                    throw new RuntimeException("当前用户正在扣除其它费用，请稍候计费");
//                }
//            } else {
//                throw new RuntimeException("无计费项目");
//            }
//        }
//        return false;
//    }
//
//
//    @Override
//    public boolean inpBillOperation(BillDto billDto) {
//        List<InpBillItem> inpBillItemList = new ArrayList<>();
//        if (billDto != null && billDto.getClinicTypeList() != null && billDto.getClinicTypeList().size() > 0) {
//            for (ClinicType item : billDto.getClinicTypeList()) {
//                boolean isPackage = DictUtils.getClinicItemClassList().stream().anyMatch(c -> c.getClassCode().equals(item.getType()));
//                for (ClinicItem item1 : item.getClinicItemList()) {
//                    {
//                        if (isPackage) {
//                            EntityWrapper<ClinicItemBill> clinicItemBillEntityWrapper = new EntityWrapper<>();
//                            clinicItemBillEntityWrapper.eq("clinic_item_class", item.getType()).eq("clinic_item_code", item1.getClinicItemCode());
//                            List<ClinicItemBill> clinicItemBills = queryClinicItemMapper.selectList(clinicItemBillEntityWrapper);
//
//                            inpBillItemList.addAll(clinicItemBills.stream().map(c -> setNewObjectinp(c.getChargeItemClass(), c.getChargeItemCode(), c.getAmount() * item1.getAmount(),
//                                    item1.getPerformedBy(), billDto.getVisitId(), c.getChargeItemNo(), item1.getOperatorNo(), item1.getOrderedBy())).collect(Collectors.toList()));
//                        } else {
//                            inpBillItemList.add(setNewObjectinp(item1.getItemClass(), item1.getPriceCode(), item1.getAmount(), item1.getPerformedBy(), billDto.getVisitId(), "1", item1.getOperatorNo(), item1.getOrderedBy()));
//                        }
//                    }
//                }
//            }
//            List<InpBillItem> getinpBillDetail = getinpBillDetail(inpBillItemList, billDto.getChargeType());
//            List<ChargePriceSchedule> chargePriceScheduleList = DictUtils.getChargePriceScheduleList();
//            ChargePriceSchedule chargePriceSchedule = chargePriceScheduleList.stream().filter(c -> c.getChargeType().equals(billDto.getChargeType()) && "1".equals(c.getChargeSpecialIndicator())).findFirst().orElse(null);
//            List<InpBillItem> inpBillItemList1 = new ArrayList<>();
//            List<InpBillItem> inpBillItemList2 = new ArrayList<>();
//
//            if (chargePriceSchedule != null) {
//                inpBillItemList1 = (List<InpBillItem>) canculateChargesExcept(getinpBillDetail, billDto.getChargeType(), billDto.getOutpOrinp());
//                inpBillItemList2 = (List<InpBillItem>) canculateChargesSpecial(getinpBillDetail, billDto.getChargeType(), billDto.getOutpOrinp());
//
//                if (getinpBillDetail != null && getinpBillDetail.size() > 0) {
//                    for (InpBillItem inpBillItem : getinpBillDetail) {
//                        inpBillItem.setCharges(Math.round(inpBillItem.getCosts() * inpBillItem.getAmount() * (chargePriceSchedule.getPriceCoffNumerator() / chargePriceSchedule.getPriceCoffDenominator()) * 100) * 0.01d);
//                        inpBillItem.setCosts(Math.round(inpBillItem.getCosts() * inpBillItem.getAmount() * 100) * 0.01d);
//                    }
//                }
//            } else {
//                if (getinpBillDetail != null && getinpBillDetail.size() > 0) {
//                    for (InpBillItem inpBillItem : getinpBillDetail) {
//                        inpBillItem.setCharges(Math.round(inpBillItem.getCosts() * inpBillItem.getAmount() * 100) * 0.01d);
//                        inpBillItem.setCosts(Math.round(inpBillItem.getCosts() * inpBillItem.getAmount() * 100) * 0.01d);
//                    }
//                }
//            }
//            getinpBillDetail.addAll(inpBillItemList1);
//            getinpBillDetail.addAll(inpBillItemList2);
//            DoubleSummaryStatistics total = null;
//            if (getinpBillDetail != null && getinpBillDetail.size() > 0) {
//                total = getinpBillDetail.stream().mapToDouble(BillItem::getCharges).summaryStatistics();
//                //锁住用户信息
//                if (RedisUtils.tryGetDistributedLock(String.format(PATIENT_KEY, billDto.getPatientId()), "1", 10000)) {
//                    try {
//                        PatientRemain inpPatientRemain = patientRemainMapper.getInpPatientRemain(billDto.getPatientId());
//                        if ("2".equals(getArrears(billDto.getPatientId(), billDto.getVisitId(), inpPatientRemain.getPrepay(), inpPatientRemain.getCharge(), inpPatientRemain.getPrepayRemain()))) {
//                            throw new RuntimeException("该患者已欠费");
//                        } else {
//                            if (total.getSum() > inpPatientRemain.getPrepayRemain()) {
//                                for (InpBillItem inpBillItem : getinpBillDetail) {
//                                    Integer insert = inpPriceMapper.insert(inpBillItem);
//                                    if (insert < 1) {
//                                        log.info("插入住院费用明细异常：" + JSON.toJSONString(inpBillItem));
//                                        throw new RuntimeException("当前费用插入异常，请联系管理员查看日志");
//                                    }
//                                }
//                                return true;
//                            } else {
////                            throw new RuntimeException("余额不足请充值");
//                            }
//                        }
//                    } catch (Exception e) {
//                        throw new RuntimeException(e.getMessage());
//                    } finally {
//                        RedisUtils.releaseDistributedLock(String.format(PATIENT_KEY, billDto.getPatientId()), "1");
//                    }
//                } else {
//                    throw new RuntimeException("当前用户正在扣除其它费用，请稍候计费");
//                }
//            } else {
//                throw new RuntimeException("无计费项目");
//            }
//        }
//        return false;
//    }
//
//    private String getArrears(String patientId, String visitId, double prepay, double charge, double remain) {
//        return "1";
//    }
//
//    /**
//     * 获取各元素对应的费用
//     *
//     * @param outpBillItemList
//     * @param chargeType       费目类别
//     * @return 处理完后返回带费用的项目List
//     */
//    private List<OutpBillitem> getOutpBillDetail(List<OutpBillitem> outpBillItemList, String chargeType) {
//        List<String> itemCodeList = outpBillItemList.stream().map(OutpBillitem::getItemCode).distinct().collect(Collectors.toList());
//        List<String> itemClassList = outpBillItemList.stream().map(OutpBillitem::getItemClass).distinct().collect(Collectors.toList());
//        List<String> itemSpecList = outpBillItemList.stream().map(OutpBillitem::getItemSpec).distinct().collect(Collectors.toList());
//        List<QueryBillItemPrice> queryBillItemPrices = new ArrayList<>();
//        Optional<ChargeType> chargeTypeIndicator = DictUtils.getChargeTypeList().stream().filter(c -> c.getChargeTypeName().equals(chargeType)).findFirst();
//        if (chargeTypeIndicator.isPresent()) {
//            queryBillItemPrices = queryBillItemPriceMapper.getAllNeedPrice(chargeTypeIndicator.get().getChargePriceIndicator() == 0 ?
//                    "PRICE" : chargeTypeIndicator.get().getChargePriceIndicator() == 1 ?
//                    "PREFER_PRICE" : "FOREIGNER_PRICE", itemCodeList, itemSpecList, itemClassList);
//
//            for (OutpBillitem item : outpBillItemList) {
//                Optional<QueryBillItemPrice> first = queryBillItemPrices.stream().filter(c -> item.getItemClass().equals(c.getItemClass()) && item.getItemSpec().equals(c.getItemSpec()) && item.getItemCode().equals(c.getItemCode())).findFirst();
//                if (first.isPresent()) {
//                    item.setCosts(first.get().getPrice());
//                    item.setClassOnRcpt(first.get().getClassOnOutpRcpt());
//                    item.setItemName(first.get().getItemName());
//                    item.setUnits(first.get().getUnits());
//
//                }
//            }
//            return outpBillItemList;
//        }
//        return null;
//    }
//
//    private List<InpBillItem> getinpBillDetail(List<InpBillItem> inpBillItemList, String chargeType) {
//        List<String> itemCodeList = inpBillItemList.stream().map(InpBillItem::getItemCode).distinct().collect(Collectors.toList());
//        List<String> itemClassList = inpBillItemList.stream().map(InpBillItem::getItemClass).distinct().collect(Collectors.toList());
//        List<String> itemSpecList = inpBillItemList.stream().map(InpBillItem::getItemSpec).distinct().collect(Collectors.toList());
//        List<QueryBillItemPrice> queryBillItemPrices = new ArrayList<>();
//        Optional<ChargeType> chargeTypeIndicator = DictUtils.getChargeTypeList().stream().filter(c -> c.getChargeTypeName().equals(chargeType)).findFirst();
//        if (chargeTypeIndicator.isPresent()) {
//            queryBillItemPrices = queryBillItemPriceMapper.getAllNeedPrice(chargeTypeIndicator.get().getChargePriceIndicator() == 0 ?
//                    "PRICE" : chargeTypeIndicator.get().getChargePriceIndicator() == 1 ?
//                    "PREFER_PRICE" : "FOREIGNER_PRICE", itemCodeList, itemSpecList, itemClassList);
//
//            for (InpBillItem item : inpBillItemList) {
//                Optional<QueryBillItemPrice> first = queryBillItemPrices.stream().filter(c -> item.getItemClass().equals(c.getItemClass()) && item.getItemSpec().equals(c.getItemSpec()) && item.getItemCode().equals(c.getItemCode())).findFirst();
//                if (first.isPresent()) {
//                    item.setCosts(first.get().getPrice());
//                    item.setClassOnInpRcpt(first.get().getClassOnOutpRcpt());
//                    item.setItemName(first.get().getItemName());
//                    item.setUnits(first.get().getUnits());
//
//                }
//            }
//            return inpBillItemList;
//        }
//        return null;
//    }
//
//    private OutpBillitem setNewObject(String itemClass, String itemCode, double totalAmount, String preformedBy, String visitNo, Date visitDate, String itemNo) {
//        OutpBillitem outpBillitem = new OutpBillitem();
//        outpBillitem.setItemClass(itemClass);
//        outpBillitem.setItemCode(itemCode);
//        outpBillitem.setAmount(totalAmount);
//        outpBillitem.setPerformedBy(preformedBy);
//        outpBillitem.setVisitDate(visitDate);
//        outpBillitem.setVisitNo(visitNo);
//        outpBillitem.setItemNo(itemNo);
//        return outpBillitem;
//    }
//
//    private InpBillItem setNewObjectinp(String itemClass, String itemCode, double totalAmount, String preformedBy, String visitId, String itemNo, String operatorNo, String orderedBy) {
//        InpBillItem inpBillItem = new InpBillItem();
//        inpBillItem.setItemClass(itemClass);
//        inpBillItem.setItemCode(itemCode);
//        inpBillItem.setAmount(totalAmount);
//        inpBillItem.setPerformedBy(preformedBy);
//        inpBillItem.setVisitId(visitId);
//        inpBillItem.setItemNo(itemNo);
//        inpBillItem.setOperatorNo(operatorNo);
//        inpBillItem.setOrderedBy(orderedBy);
//        inpBillItem.setBillingDateTime(new Date());
//        return inpBillItem;
//    }
//
//
//    /**
//     * 计算单个项目种按照排斥的实际收费（处理打折）
//     *
//     * @param billItemList
//     * @param chargeType
//     * @return
//     */
//    private List<? extends BillItem> canculateChargesExcept(List<? extends BillItem> billItemList, String chargeType, String outpOrInp) {
//        List<ChargeSpecialExcept> chargeSpecialExceptList = DictUtils.getChargeSpecialExceptList();
//        List<String> itemClassList = billItemList.stream().map(BillItem::getItemClass).distinct().collect(Collectors.toList());
//        List<ChargeSpecialExcept> orderCharges = chargeSpecialExceptList.stream().filter(c -> c.getChargeType().equals(chargeType) && itemClassList.contains(c.getItemClass()) && (!(outpOrInp.equals(c.getFlag()) || "3".equals(c.getFlag())))).collect(Collectors.toList());
//
//        List<BillItem> repelledBillItemList = new ArrayList<>();
//        for (BillItem item : billItemList) {
//            ChargeSpecialExcept chargeSpecialExcept = orderCharges.stream().filter(c ->
//                    c.getItemClass().equals(item.getItemClass())
//                            && ("*".equals(c.getItemCode()) || c.getItemCode().equals(item.getItemCode()))
//                            && ("*".equals(c.getItemSpec()) || c.getItemSpec().equals(item.getItemSpec()))
//            ).findFirst().orElse(null);
//            if (chargeSpecialExcept != null) {
//                item.setCharges(Math.round(item.getCosts() * item.getAmount() * (chargeSpecialExcept.getProportionNumerator() / chargeSpecialExcept.getProportionDenominator()) * 100) * 0.01d);
//                item.setCosts(Math.round(item.getCosts() * item.getAmount() * 100) * 0.01d);
//                repelledBillItemList.add(item);
//            }
//        }
//        for (BillItem item : repelledBillItemList) {
//            if (billItemList.contains(item)) {
//                billItemList.remove(item);
//            }
//        }
//
//        return repelledBillItemList;
//    }
//
//    /**
//     * 计算单个项目种按照特殊项目的实际收费（处理打折）
//     *
//     * @param billItemList
//     * @param chargeType
//     * @param outpOrinp
//     * @return
//     */
//    private List<? extends BillItem> canculateChargesSpecial(List<? extends BillItem> billItemList, String chargeType, String outpOrinp) {
//        List<ChargeSpecialItem> chargeSpecialItemList = DictUtils.getChargeSpecialItemList();
//        List<String> itemClassList = billItemList.stream().map(BillItem::getItemClass).distinct().collect(Collectors.toList());
//        List<ChargeSpecialItem> orderCharges = chargeSpecialItemList.stream().filter(c -> c.getChargeType().equals(chargeType) && itemClassList.contains(c.getItemClass()) && (outpOrinp.equals(c.getFlag()) || "3".equals(c.getFlag()))).collect(Collectors.toList());
//
//        List<BillItem> repelledBillItemList = new ArrayList<>();
//        for (BillItem item : billItemList) {
//            ChargeSpecialItem chargeSpecialItem = orderCharges.stream().filter(c ->
//                    c.getItemClass().equals(item.getItemClass())
//                            && ("*".equals(c.getItemCode()) || c.getItemCode().equals(item.getItemCode()))
//                            && ("*".equals(c.getItemSpec()) || c.getItemSpec().equals(item.getItemSpec()))
//            ).findFirst().orElse(null);
//            if (chargeSpecialItem != null) {
//                item.setCharges(Math.round(item.getCosts() * item.getAmount() * (chargeSpecialItem.getProportionNumerator() / chargeSpecialItem.getProportionDenominator()) * 100) * 0.01d);
//                item.setCosts(Math.round(item.getCosts() * item.getAmount() * 100) * 0.01d);
//                repelledBillItemList.add(item);
//            }
//        }
//        for (BillItem item : repelledBillItemList) {
//            if (billItemList.contains(item)) {
//                billItemList.remove(item);
//            }
//        }
//
//        return repelledBillItemList;
//    }
//
//
//}
package com.hospital.billcommon.module.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hospital.api.RemoteDictionaryService;
import com.hospital.billcommon.module.controller.dto.BillDto;
import com.hospital.billcommon.module.controller.dto.ClinicItem;
import com.hospital.billcommon.module.controller.dto.ClinicType;
import com.hospital.billcommon.module.entity.*;
import com.hospital.billcommon.module.mapper.*;
import com.hospital.billcommon.module.service.BillService;
import com.hospital.common.core.module.entity.ChargeSpecialExceptDict;
import com.hospital.common.core.module.entity.ChargeSpecialItemDict;
import com.hospital.common.core.module.entity.ChargeTypeDict;
import com.hospital.common.core.module.entity.query.BillItem;
import com.hospital.common.core.utils.DateUtil;
import com.hospital.common.core.utils.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author lvyao
 * @description 扣费实现类
 * @date 2020/5/7
 */
@Service
@Transactional(rollbackFor = Exception.class)
@Slf4j
public class BillServiceImpl extends ServiceImpl<OutpPriceMapper, OutpBillitem> implements BillService {

    private final String PATIENT_KEY = "patient_id_%s";

    @Resource
    private PatientRemainMapper patientRemainMapper;
    @Resource
    private QueryClinicItemMapper queryClinicItemMapper;
    @Resource
    private OutpPriceMapper outpPriceMapper;

    @Resource
    private InpPriceMapper inpPriceMapper;

    @Resource
    private QueryBillItemPriceMapper queryBillItemPriceMapper;


    @Resource
    private InpGetMaxItemNoMapper inpGetMaxItemNoMapper;

    @Resource
    private RemoteDictionaryService remoteDictionaryService;

    @Resource
    private InpGetChargeAndInsuranceTypeMapper inpGetChargeAndInsuranceTypeMapper;

    @Resource
    private ChargePriceScheduleMapper chargePriceScheduleMapper;

    @Resource
    private InpGetArrearsParamMapper inpGetArrearsParamMapper;

    @Override
    public boolean outpBillOperation(BillDto billDto) throws ParseException ,RuntimeException{
        List<OutpBillitem> outpBillItemList = new ArrayList<>();
        if (billDto != null && billDto.getClinicTypeList() != null && billDto.getClinicTypeList().size() > 0) {
            for (ClinicType item : billDto.getClinicTypeList()) {
                boolean isPackage = remoteDictionaryService.getclinicItemClassDictList().stream().anyMatch(c -> c.getClassCode().equals(item.getType()));
                int i = 1;
                for (ClinicItem item1 : item.getClinicItemList()) {
                    String primaryDate = item1.getPrimaryDate();
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    Timestamp visit = DateUtil.format(sdf.parse(primaryDate));
                    {
                        if (isPackage) {
                            QueryWrapper<ClinicItemBill> clinicItemBillEntityWrapper = new QueryWrapper<>();
                            clinicItemBillEntityWrapper.eq("clinic_item_class", item.getType()).eq("clinic_item_code", item1.getClinicItemCode());
                            List<ClinicItemBill> clinicItemBills = queryClinicItemMapper.selectList(clinicItemBillEntityWrapper);
                            Timestamp finalVisit = visit;
                            outpBillItemList.addAll(clinicItemBills.stream().map(c -> setNewObject(c.getChargeItemClass(), c.getChargeItemCode(), c.getChargeItemSpec(), c.getAmount() * item1.getAmount(),
                                    item1.getPerformedBy(), item1.getPrimaryNo(), finalVisit, c.getChargeItemNo())).collect(Collectors.toList()));
                        } else {
                            outpBillItemList.add(setNewObject(item1.getItemClass(), item1.getPriceCode(), item1.getItemSpec(), item1.getAmount(), item1.getPerformedBy(), item1.getPrimaryNo(), visit, String.valueOf(i)));
                            i++;
                        }
                    }

                }
            }
            List<OutpBillitem> outpBillDetail = getOutpBillDetail(outpBillItemList, billDto.getChargeType());
            List<ChargePriceSchedule> chargePriceScheduleList = chargePriceScheduleMapper.selectList(new QueryWrapper<>());
            ChargePriceSchedule chargePriceSchedule = chargePriceScheduleList.stream().filter(c -> c.getChargeType().equals(billDto.getChargeType()) && c.getChargeSpecialIndicator() == 1).findFirst().orElse(null);
            List<OutpBillitem> outpBillitemList = new ArrayList<>();
            List<OutpBillitem> outpBillitemList1 = new ArrayList<>();

            if (chargePriceSchedule != null) {
                outpBillitemList = (List<OutpBillitem>) canculateChargesExcept(outpBillDetail, billDto.getChargeType(), billDto.getOutpOrinp());
                outpBillitemList1 = (List<OutpBillitem>) canculateChargesSpecial(outpBillDetail, billDto.getChargeType(), billDto.getOutpOrinp());
                if (outpBillDetail != null && outpBillDetail.size() > 0) {
                    for (OutpBillitem outpBillitem : outpBillDetail) {
                        outpBillitem.setCharges(Math.round(outpBillitem.getCosts() * outpBillitem.getAmount() * (chargePriceSchedule.getPriceCoeffNumerator() / chargePriceSchedule.getPriceCoeffDenominator()) * 100) * 0.01d);
                        outpBillitem.setCosts(Math.round(outpBillitem.getCosts() * outpBillitem.getAmount() * 100) * 0.01d);
                    }
                }
            } else {
                if (outpBillDetail != null && outpBillDetail.size() > 0) {
                    for (OutpBillitem outpBillitem : outpBillDetail) {
                        outpBillitem.setCharges(Math.round(outpBillitem.getCosts() * outpBillitem.getAmount() * 100) * 0.01d);
                        outpBillitem.setCosts(Math.round(outpBillitem.getCosts() * outpBillitem.getAmount() * 100) * 0.01d);
                    }
                }
            }
            outpBillDetail.addAll(outpBillitemList1);
            outpBillDetail.addAll(outpBillitemList);
            DoubleSummaryStatistics total = null;
            if (outpBillDetail != null && outpBillDetail.size() > 0) {
                total = outpBillDetail.stream().mapToDouble(BillItem::getCharges).summaryStatistics();
                //锁住用户信息
                if (RedisUtils.tryGetDistributedLock(String.format(PATIENT_KEY, billDto.getPatientId()), "1", 10000)) {
                    try {
                        PatientRemain outpPatientRemain = patientRemainMapper.getOutpPatientRemain(billDto.getPatientId());
                        if (total.getSum() < outpPatientRemain.getPrepayRemain()) {
                            for (OutpBillitem outpBillitem : outpBillDetail.stream().filter(c -> c.isCount()).collect(Collectors.toList())) {
                                Integer insert = outpPriceMapper.insert(outpBillitem);
                                if (insert < 1) {
//                                    log.info("插入门诊费用明细异常：" + JSON.toJSONString(outpBillitem));
                                    throw new RuntimeException("当前费用插入异常，请联系管理员查看日志");
                                }
                            }
                            return true;
                        } else {
                            throw new RuntimeException("余额不足请充值");
                        }
                    } catch (Exception e) {
                        throw new RuntimeException(e.getMessage());
                    } finally {
                        RedisUtils.releaseDistributedLock(String.format(PATIENT_KEY, billDto.getPatientId()), "1");
                    }
                } else {
                    throw new RuntimeException("当前用户正在扣除其它费用，请稍候计费");
                }
            } else {
                throw new RuntimeException("无计费项目");
            }
        }
        return false;
    }


    @Override
    public boolean inpBillOperation(BillDto billDto) {
        List<InpBillItem> inpBillItemList = new ArrayList<>();
        if (billDto != null && billDto.getClinicTypeList() != null && billDto.getClinicTypeList().size() > 0) {
            for (ClinicType item : billDto.getClinicTypeList()) {
                boolean isPackage = remoteDictionaryService.getclinicItemClassDictList().stream().anyMatch(c -> c.getClassCode().equals(item.getType()));
                QueryWrapper<InpGetMaxItemNo> inpGetMaxItemNoEntityWrapper = new QueryWrapper<>();
                inpGetMaxItemNoEntityWrapper.eq("patient_id", billDto.getPatientId()).eq("visit_id", billDto.getVisitId()).orderByDesc("item_no");
                InpGetMaxItemNo inpGetMaxItemNo = inpGetMaxItemNoMapper.selectList(inpGetMaxItemNoEntityWrapper).stream().findFirst().orElse(null);
                int beforItemNo = 1;
                if (inpGetMaxItemNo != null) {
                    beforItemNo = inpGetMaxItemNo.getItemNo() + 1;
                }
                for (ClinicItem item1 : item.getClinicItemList()) {
                    {
                        if (isPackage) {
                            QueryWrapper<ClinicItemBill> clinicItemBillEntityWrapper = new QueryWrapper<>();
                            clinicItemBillEntityWrapper.eq("clinic_item_class", item.getType()).eq("clinic_item_code", item1.getClinicItemCode());
                            List<ClinicItemBill> clinicItemBills = queryClinicItemMapper.selectList(clinicItemBillEntityWrapper);
                            inpBillItemList.addAll(clinicItemBills.stream().map(c -> setNewObjectinp(billDto.getPatientId(),c.getChargeItemClass(), c.getChargeItemCode(), c.getChargeItemSpec(), c.getAmount() * item1.getAmount(),
                                    item1.getPerformedBy(), billDto.getVisitId(), c.getChargeItemNo(), item1.getOperatorNo(), item1.getOrderedBy(),String.valueOf(inpGetMaxItemNo.getItemNo()))).collect(Collectors.toList()));
                        } else {
                            inpBillItemList.add(setNewObjectinp(billDto.getPatientId(),item1.getItemClass(), item1.getPriceCode(), item1.getItemSpec(), item1.getAmount(), item1.getPerformedBy(), billDto.getVisitId(), String.valueOf(beforItemNo), item1.getOperatorNo(), item1.getOrderedBy(),String.valueOf(0)));
                        }
                        beforItemNo++;
                    }
                }
            }
            List<InpBillItem> getinpBillDetail = getinpBillDetail(inpBillItemList, billDto.getChargeType());
            List<ChargePriceSchedule> chargePriceScheduleList = chargePriceScheduleMapper.selectList(new QueryWrapper<>());
            ChargePriceSchedule chargePriceSchedule = chargePriceScheduleList.stream().filter(c -> c.getChargeType().equals(billDto.getChargeType()) && "1".equals(c.getChargeSpecialIndicator())).findFirst().orElse(null);
            List<InpBillItem> inpBillItemList1 = new ArrayList<>();
            List<InpBillItem> inpBillItemList2 = new ArrayList<>();

            if (chargePriceSchedule != null) {
                inpBillItemList1 = (List<InpBillItem>) canculateChargesExcept(getinpBillDetail, billDto.getChargeType(), billDto.getOutpOrinp());
                inpBillItemList2 = (List<InpBillItem>) canculateChargesSpecial(getinpBillDetail, billDto.getChargeType(), billDto.getOutpOrinp());

                if (getinpBillDetail != null && getinpBillDetail.size() > 0) {
                    for (InpBillItem inpBillItem : getinpBillDetail) {
                        inpBillItem.setCharges(Math.round(inpBillItem.getCosts() * inpBillItem.getAmount() * (chargePriceSchedule.getPriceCoeffNumerator() / chargePriceSchedule.getPriceCoeffDenominator()) * 100) * 0.01d);
                        inpBillItem.setCosts(Math.round(inpBillItem.getCosts() * inpBillItem.getAmount() * 100) * 0.01d);
                    }
                }
            } else {
                if (getinpBillDetail != null && getinpBillDetail.size() > 0) {
                    for (InpBillItem inpBillItem : getinpBillDetail) {
                        inpBillItem.setCharges(Math.round(inpBillItem.getCosts() * inpBillItem.getAmount() * 100) * 0.01d);
                        inpBillItem.setCosts(Math.round(inpBillItem.getCosts() * inpBillItem.getAmount() * 100) * 0.01d);
                    }
                }
            }
            getinpBillDetail.addAll(inpBillItemList1);
            getinpBillDetail.addAll(inpBillItemList2);
            DoubleSummaryStatistics total = null;
            if (getinpBillDetail != null && getinpBillDetail.size() > 0) {
                total = getinpBillDetail.stream().mapToDouble(BillItem::getCharges).summaryStatistics();
                //锁住用户信息
                if (RedisUtils.tryGetDistributedLock(String.format(PATIENT_KEY, billDto.getPatientId()), "1", 10000)) {
                    try {
                        PatientRemain inpPatientRemain = patientRemainMapper.getInpPatientRemain(billDto.getPatientId());
                        if ("2".equals(getArrears(billDto.getPatientId(), billDto.getVisitId(), inpPatientRemain.getPrepay(), inpPatientRemain.getCharge(), inpPatientRemain.getPrepayRemain(), billDto.getChargeType()))) {
                            throw new RuntimeException("该患者已欠费");
                        } else {
                            for (InpBillItem inpBillItem : getinpBillDetail.stream().filter(c -> c.isCount()).collect(Collectors.toList())) {
                                Integer insert = inpPriceMapper.insert(inpBillItem);
                                if (insert < 1) {
//                                    log.info("插入住院费用明细异常：" + JSON.toJSONString(inpBillItem));
                                    throw new RuntimeException("当前费用插入异常，请联系管理员查看日志");
                                }
                            }
                            return true;
                        }
                    } catch (Exception e) {
                        throw new RuntimeException(e.getMessage());
                    } finally {
                        RedisUtils.releaseDistributedLock(String.format(PATIENT_KEY, billDto.getPatientId()), "1");
                    }
                } else {
                    throw new RuntimeException("当前用户正在扣除其它费用，请稍候计费");
                }
            } else {
                throw new RuntimeException("无计费项目");
            }
        }
        return false;
    }

    private String getArrears(String patientId, String visitId, double prepay, double charge, double remain, String chargeType) {

        List<InpGetChargeAndInsuranceType> inpChargeAndSubList = inpGetChargeAndInsuranceTypeMapper.selectList(new QueryWrapper<InpGetChargeAndInsuranceType>()
                .eq("patient_id",patientId).eq("visit_id",visitId));
        InpGetChargeAndInsuranceType inpChargeAndSub=inpChargeAndSubList.stream().findFirst().orElse(null);
        List<InpGetArrearsParam> inpGetArrearsParamList = inpGetArrearsParamMapper.selectList(new QueryWrapper<>());
        List<InpGetArrearsParam> inpGetArrearsParams = null;
        InpGetArrearsParam inpGetArrearsParam = null;
        if (inpChargeAndSub != null) {
            if (inpChargeAndSub.getInsuranceSubTypeName() == null) {
                inpGetArrearsParams = inpGetArrearsParamList.stream().filter(c -> c.getInsuranceTypeName().equals(chargeType)).collect(Collectors.toList());
                if (inpGetArrearsParams.size() == 1) {
                    inpGetArrearsParam = inpGetArrearsParams.get(0);
                } else {
                    inpGetArrearsParam = inpGetArrearsParamList.stream().filter(c -> "自费".equals(c.getInsuranceTypeName())).findFirst().orElse(null);
                }
            } else {
                inpGetArrearsParam = inpGetArrearsParamList.stream().filter(c -> c.getInsuranceTypeName().equals(chargeType) && c.getInsuranceSubTypeName().equals(inpChargeAndSub.getInsuranceSubTypeName())).findFirst().orElse(null);
            }
        }
        if (null != inpGetArrearsParam) {
            if (null != inpGetArrearsParam.getWarningLevel() && null != inpGetArrearsParam.getStopLevel()) {
                double d = prepay * 100 / charge;
                if (Integer.valueOf(inpGetArrearsParam.getWarningLevel()) <= d) {
                    return "0";
                } else if (Integer.valueOf(inpGetArrearsParam.getWarningLevel()) > d && d >= Integer.valueOf(inpGetArrearsParam.getStopLevel())) {
                    return "1";
                } else {
                    return "2";
                }
            } else if (null != inpGetArrearsParam.getWarningAmount() && null != inpGetArrearsParam.getStopAmount()) {
                if (prepay >= Integer.valueOf(inpGetArrearsParam.getWarningAmount())) {
                    return "0";
                } else if (prepay >= Integer.valueOf(inpGetArrearsParam.getStopAmount()) && prepay < Integer.valueOf(inpGetArrearsParam.getWarningAmount())) {
                    return "1";
                } else {
                    return "2";
                }
            }
        }
        return "0";
    }

    /**
     * 获取各元素对应的费用
     *
     * @param outpBillItemList
     * @param chargeType       费目类别
     * @return 处理完后返回带费用的项目List
     */
    private List<OutpBillitem> getOutpBillDetail(List<OutpBillitem> outpBillItemList, String chargeType) {
        List<String> itemCodeList = outpBillItemList.stream().map(OutpBillitem::getItemCode).distinct().collect(Collectors.toList());
        List<String> itemClassList = outpBillItemList.stream().map(OutpBillitem::getItemClass).distinct().collect(Collectors.toList());
        List<String> itemSpecList = outpBillItemList.stream().map(OutpBillitem::getItemSpec).distinct().collect(Collectors.toList());
        List<QueryBillItemPrice> queryBillItemPrices = new ArrayList<>();
        Optional<ChargeTypeDict> chargeTypeIndicator = remoteDictionaryService.getchargeTypeDictList().stream().filter(c -> c.getChargeTypeName().equals(chargeType)).findFirst();
        if (chargeTypeIndicator.isPresent()) {
            String columnName = chargeTypeIndicator.get().getChargePriceIndicator() == 0 ?
                    "PRICE" : chargeTypeIndicator.get().getChargePriceIndicator() == 1 ?
                    "PREFER_PRICE" : "FOREIGNER_PRICE";
            queryBillItemPrices = queryBillItemPriceMapper.getAllNeedPrice(columnName, itemCodeList, itemSpecList, itemClassList,"sysdate");

            for (OutpBillitem item : outpBillItemList) {
                List<QueryBillItemPrice> first = queryBillItemPrices.stream().filter(c -> item.getItemClass().equals(c.getItemClass()) && item.getItemSpec().equals(c.getItemSpec()) && item.getItemCode().equals(c.getItemCode())).collect(Collectors.toList());
                if (first != null && first.size() > 0) {
                    item.setCosts(first.get(0).getPrice());
                    item.setClassOnRcpt(first.get(0).getClassOnOutpRcpt());
                    item.setItemName(first.get(0).getItemName());
                    item.setUnits(first.get(0).getUnits());
                    item.setCount(true);
                }
            }
            return outpBillItemList;
        }
        return null;
    }

    private List<InpBillItem> getinpBillDetail(List<InpBillItem> inpBillItemList, String chargeType) {
        List<String> itemCodeList = inpBillItemList.stream().map(InpBillItem::getItemCode).distinct().collect(Collectors.toList());
        List<String> itemClassList = inpBillItemList.stream().map(InpBillItem::getItemClass).distinct().collect(Collectors.toList());
        List<String> itemSpecList = inpBillItemList.stream().map(InpBillItem::getItemSpec).distinct().collect(Collectors.toList());
        List<QueryBillItemPrice> queryBillItemPrices = new ArrayList<>();
        Optional<ChargeTypeDict> chargeTypeIndicator = remoteDictionaryService.getchargeTypeDictList().stream().filter(c -> c.getChargeTypeName().equals(chargeType)).findFirst();
        if (chargeTypeIndicator.isPresent()) {
            queryBillItemPrices = queryBillItemPriceMapper.getAllNeedPrice(chargeTypeIndicator.get().getChargePriceIndicator() == 0 ?
                    "PRICE" : chargeTypeIndicator.get().getChargePriceIndicator() == 1 ?
                    "PREFER_PRICE" : "FOREIGNER_PRICE", itemCodeList, itemSpecList, itemClassList,"sysdate");

            for (InpBillItem item : inpBillItemList) {
                Optional<QueryBillItemPrice> first = queryBillItemPrices.stream().filter(c -> item.getItemClass().equals(c.getItemClass()) && item.getItemSpec().equals(c.getItemSpec()) && item.getItemCode().equals(c.getItemCode())).findFirst();
                if (first.isPresent()) {
                    item.setCosts(first.get().getPrice());
                    item.setClassOnInpRcpt(first.get().getClassOnOutpRcpt());
                    item.setItemName(first.get().getItemName());
                    item.setUnits(first.get().getUnits());
                    item.setCount(true);
                }
            }
            return inpBillItemList;
        }
        return null;
    }

    private OutpBillitem setNewObject(String itemClass, String itemCode, String itemSpec, double totalAmount, String preformedBy, String visitNo, Timestamp visitDate, String itemNo) {
        OutpBillitem outpBillitem = new OutpBillitem();
        outpBillitem.setItemClass(itemClass);
        outpBillitem.setItemCode(itemCode);
        outpBillitem.setItemSpec(itemSpec);
        outpBillitem.setAmount(totalAmount);
        outpBillitem.setPerformedBy(preformedBy);
        outpBillitem.setVisitDate(visitDate);
        outpBillitem.setVisitNo(visitNo);
        outpBillitem.setItemNo(itemNo);
        return outpBillitem;
    }

    private InpBillItem setNewObjectinp(String patientId,String itemClass, String itemCode, String itemSpec, double totalAmount, String preformedBy, String visitId, String itemNo, String operatorNo, String orderedBy,String beforItemNO) {
        InpBillItem inpBillItem = new InpBillItem();
        inpBillItem.setPatientId(patientId);
        inpBillItem.setItemClass(itemClass);
        inpBillItem.setItemCode(itemCode);
        inpBillItem.setItemSpec(itemSpec);
        inpBillItem.setAmount(totalAmount);
        inpBillItem.setPerformedBy(preformedBy);
        inpBillItem.setVisitId(visitId);
        itemNo = String.valueOf(Integer.valueOf(itemNo) + Integer.valueOf(beforItemNO));
        inpBillItem.setItemNo(itemNo);
        inpBillItem.setOperatorNo(operatorNo);
        inpBillItem.setOrderedBy(orderedBy);
        inpBillItem.setBillingDateTime(new Date());
        return inpBillItem;
    }


    /**
     * 计算单个项目种按照排斥的实际收费（处理打折）
     *
     * @param billItemList
     * @param chargeType
     * @return
     */
    private List<? extends BillItem> canculateChargesExcept(List<? extends BillItem> billItemList, String chargeType, String outpOrInp) {
        List<ChargeSpecialExceptDict> chargeSpecialExceptList = remoteDictionaryService.getchargeSpecialExceptDictList();
        List<String> itemClassList = billItemList.stream().map(BillItem::getItemClass).distinct().collect(Collectors.toList());
        List<ChargeSpecialExceptDict> orderCharges = chargeSpecialExceptList.stream().filter(c -> c.getChargeType().equals(chargeType) && itemClassList.contains(c.getItemClass()) && (!(outpOrInp.equals(c.getFlag()) || "3".equals(c.getFlag())))).collect(Collectors.toList());

        List<BillItem> repelledBillItemList = new ArrayList<>();
        for (BillItem item : billItemList) {
            ChargeSpecialExceptDict chargeSpecialExcept = orderCharges.stream().filter(c ->
                    c.getItemClass().equals(item.getItemClass())
                            && ("*".equals(c.getItemCode()) || c.getItemCode().equals(item.getItemCode()))
                            && ("*".equals(c.getItemSpec()) || c.getItemSpec().equals(item.getItemSpec()))
            ).findFirst().orElse(null);
            if (chargeSpecialExcept != null) {
                item.setCharges(Math.round(item.getCosts() * item.getAmount() * ((double) chargeSpecialExcept.getProportionNumerator() / (double) chargeSpecialExcept.getProportionDenominator()) * 100) * 0.01d);
                item.setCosts(Math.round(item.getCosts() * item.getAmount() * 100) * 0.01d);
                repelledBillItemList.add(item);
            }
        }
        for (BillItem item : repelledBillItemList) {
            if (billItemList.contains(item)) {
                billItemList.remove(item);
            }
        }

        return repelledBillItemList;
    }

    /**
     * 计算单个项目种按照特殊项目的实际收费（处理打折）
     *
     * @param billItemList
     * @param chargeType
     * @param outpOrinp
     * @return
     */
    private List<? extends BillItem> canculateChargesSpecial(List<? extends BillItem> billItemList, String chargeType, String outpOrinp) {
        List<ChargeSpecialItemDict> chargeSpecialItemList = remoteDictionaryService.getchargeSpecialItemDictList();
        List<String> itemClassList = billItemList.stream().map(BillItem::getItemClass).distinct().collect(Collectors.toList());
        List<ChargeSpecialItemDict> orderCharges = chargeSpecialItemList.stream().filter(c -> c.getChargeType().equals(chargeType) && itemClassList.contains(c.getItemClass()) && (outpOrinp.equals(c.getFlag()) || "3".equals(c.getFlag()))).collect(Collectors.toList());

        List<BillItem> repelledBillItemList = new ArrayList<>();
        for (BillItem item : billItemList) {
            ChargeSpecialItemDict chargeSpecialItem = orderCharges.stream().filter(c ->
                    c.getItemClass().equals(item.getItemClass())
                            && ("*".equals(c.getItemCode()) || c.getItemCode().equals(item.getItemCode()))
                            && ("*".equals(c.getItemSpec()) || c.getItemSpec().equals(item.getItemSpec()))
            ).findFirst().orElse(null);
            if (chargeSpecialItem != null) {
                double a = ((double) chargeSpecialItem.getProportionNumerator() / (double) chargeSpecialItem.getProportionDenominator());
                item.setCharges(Math.round(item.getCosts() * item.getAmount() * a * 100) * 0.01d);
                item.setCosts(Math.round(item.getCosts() * item.getAmount() * 100) * 0.01d);
                repelledBillItemList.add(item);
            }
        }
        for (BillItem item : repelledBillItemList) {
            if (billItemList.contains(item)) {
                billItemList.remove(item);
            }
        }

        return repelledBillItemList;
    }


}

